include("addresses.lua");
include("functions.lua");
-- Note: We get 'char' and 'macro' data from functions.lua
-- because it is used in other scripts.


--[[
	Required:
	pattern		The pattern, obviously.
	mask		...The mask?
	offset		Offset from the start of the pattern that the requested data exists.
	startloc	Where to start the search, in bytes

	Optional:
	searchlen	The length, in bytes, to continue searching (default: 0xA0000)
	adjustment	How many bytes to adjust the returned value forward or backward (default: 0)
	size		The length, in bytes, of the data (default: 4)
	comment		A string of text that will be appended in the output
]]

local updatePatterns =
{
	staticbase_char = {
		pattern = getCharUpdatePattern(),
		mask = getCharUpdateMask(),
		offset = getCharUpdateOffset(),
		startloc = 0x5A0000,
	},

	staticbase_macro = {
		pattern = getMacroUpdatePattern(),
		mask = getMacroUpdateMask(),
		offset = getMacroUpdateOffset(),
		startloc = 0x700000,
	},

	staticTablePtr = {
		pattern = string.char(0x7E, 0xFF, 0x53, 0x56, 0x57, 0xA1, 0xFF, 0xFF, 0xFF, 0xFF, 0x8B, 0x3C, 0xA8, 0x8B, 0x1D),
		mask = "x?xxxx????xxxxx",
		offset = 6,
		startloc = 0x820000,
	},

	staticTableSize = {
		pattern = string.char(0x85, 0xFF, 0x74, 0x09, 0x57, 0xE8, 0xFF, 0xFF, 0xFF, 0xFF,
			0x83, 0xC4, 0x04, 0x8B, 0x15, 0xFF, 0xFF, 0xFF, 0xFF, 0x52),
		mask = "xxxxxx????xxxxx????x",
		offset = 15,
		startloc = 0x620000,
	},

	charPtr_offset = {
		pattern = string.char(0x8B, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x85, 0xC0, 0x74, 0xFF, 0x8B, 0x80),
		mask = "xx????xxx?xx",
		offset = 2,
		startloc = 0x5A0000,
	},

	charPtrMounted_offset = {
		pattern = string.char(0x83, 0x79, 0xFF, 0x00, 0x74, 0x0C, 0xF6, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x74, 0x03, 0xB0, 0x01),
		mask = "xx?xxxxx?????xxxx",
		offset = 2,
		startloc = 0x840000,
		size = 1,
	},

	pawnName_offset = {
		pattern = string.char(0xC3, 0x8D, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x8B, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x85, 0xC0, 0x75),
		mask = "xxx????xxx????xxx",
		offset = 10,
		startloc = 0x840000,
	},

	swimAddress = {
		pattern = string.char(
			0xC7, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0xC7, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
			0x89, 0x83, 0xFF, 0xFF, 0xFF, 0xFF,
			0xE9),
		mask = "xx????????xx????????xx????x",
		offset = 0,
		startloc = 0x440000,
	},

	wallAddress = {
		pattern = string.char(
			0xD9, 0x44, 0x24, 0xFF,
			0xF3, 0x0F, 0x10, 0x05, 0xFF, 0xFF, 0xFF, 0xFF,
			0xDC, 0x0D, 0xFF, 0xFF, 0xFF, 0xFF,
			0xF3, 0x0F, 0x11, 0x4C, 0x24, 0xFF),
		mask = "xxx?xxxx????xx????xxxxx?",
		offset = 8,
		startloc = 0x450000,
	},

	isInGame = {
		pattern = string.char(
			0x0F, 0x57, 0xC0,
			0x89, 0x40, 0xFF,
			0xA1, 0xFF, 0xFF, 0xFF, 0xFF,
			0xC7, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0X00, 0X00, 0X00, 0X00),
		mask = "xxxxx?x????xx????xxxx",
		offset = 13,
		startloc = 0x630000,
	},

	loadingScreen_offset = {
		pattern = string.char(0xFF, 0xD2, 0xDD, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xC6, 0x46, 0x0FF, 0xFF, 0xD9, 0x44, 0x24, 0xFF, 0xDF, 0xF1),
		mask = "xxxx????xx??xxx?xx",
		offset = 10,
		startloc = 0x7B0000,
		size = 1,
	},

	loadingScreenPtr = {
		pattern = string.char(0xFF, 0xD0, 0x80, 0x7E, 0xFF, 0xFF, 0x0F, 0x85, 0xFF, 0xFF, 0xFF, 0xFF, 0x8B, 0x0D, 0xFF, 0xFF, 0xFF, 0xFF, 0x85, 0xC9, 0x0F, 0x84),
		mask = "xxxx??xx????xx????xxxx",
		offset = 14,
		startloc = 0x5E0000,
	},
}


-- This function will attempt to automatically find the true addresses
-- from RoM, even if they have moved.
-- Only works on MicroMacro v1.0 or newer.
function findOffsets()
	-- Sort names so the output is in order
    local new_patterns = {}
	for n in pairs(updatePatterns) do
		table.insert(new_patterns, n)
	end
	table.sort(new_patterns)

    for __,name in ipairs(new_patterns) do
		local values = updatePatterns[name]

		local found = 0;
		local readFunc = nil;
		local pattern = values["pattern"];
		local mask = values["mask"];
		local offset = values["offset"];
		local startloc = values["startloc"];
		local searchlen = values["searchlen"] or 0xA0000;
		local adjustment = values["adjustment"] or 0;
		local size = values["size"] or 4;
		local comment = values["comment"] or "";

		found = findPatternInProcess(getProc(), pattern, mask, startloc, searchlen);
		if( found == 0 ) then
			error("Unable to find \'" .. name .. "\' in module.", 0);
		end

		if( size == 1 ) then
			readFunc = memoryReadUByte;
		elseif( size == 2 ) then
			readFunc = memoryReadUShort;
		elseif( size == 4 ) then
			readFunc = memoryReadUInt
		else -- default, assume 4 bytes
			readFunc = memoryReadUInt;
		end

		addresses[name] = readFunc(getProc(), found + offset) + adjustment;
		local msg = sprintf("Patched addresses.%s\t (value: 0x%X, at: 0x%X)", name, addresses[name], found + offset);
		printf(msg .. "\n");
		logMessage(msg);

		-- Special cases; record found locations
		if( name == "staticbase_char" ) then
			addresses.staticpattern_char = found;
		elseif( name == "staticbase_macro" ) then
			addresses.staticpattern_macro = found;
		elseif( name == "swimAddress" ) then
			addresses.swimAddress = found;
		end
	end

	-- Assumption-based updating.
	-- Not very accurate, but is quick-and-easy for those
	-- hard to track values.
	printf("\n\n");
	local function assumptionUpdate(name, newValue)
		local assumptionUpdateMsg = "Assuming information for \'addresses.%s\'; now 0x%X, was 0x%X\n";
		printf(assumptionUpdateMsg, name, newValue, (addresses[name] or 0));
		addresses[name] = newValue;
	end

	printf("\n");
	local function readBytesUpdate(name, address, number)
		local readBytesUpdateMsg = "Read bytes for %s at: 0x%X Bytes:%s\n"
		local bytesString = ""
		local tmp = {}
		for i = 0, number-1 do
			local tmpbyte = memoryReadUByte(getProc(),address + i)
			table.insert(tmp, tmpbyte)
			bytesString = bytesString ..string.format(" %02X", tmpbyte)
		end

		if tmp[1] == 0x90 then
			error("Patch bytes = 0x90. Please restart the game before trying to run \"rom\\update\" again.")
		end

		printf(readBytesUpdateMsg, name, address, bytesString)
		addresses[name] = tmp
	end

	readBytesUpdate("swimAddressBytes", addresses.swimAddress, 10)
end

function rewriteAddresses()
	local filename = getExecutionPath() .. "/addresses.lua";
	getProc(); -- Just to make sure we open the process first

	printf("Scanning for updated addresses...\n");
	findOffsets();
	printf("Finished.\n");

	local addresses_new = {};
	for i,v in pairs(addresses) do
		table.insert(addresses_new, {index = i, value = v});
	end

	-- Sort alphabetically by index
	local function addressSort(tab1, tab2)
		if( tab1.index < tab2.index ) then
			return true;
		end

		return false;
	end
	table.sort(addresses_new, addressSort);

	local file = io.open(filename, "w");

	file:write(
		sprintf("-- Auto-generated by update.lua\n") ..
		"addresses = {\n"
	);

	for i,v in pairs(addresses_new) do
		local comment = "";
		if( updatePatterns[v.index] ) then
			local tmp = updatePatterns[v.index].comment;
			if( tmp ) then
				comment = "\t--[[ " .. tmp .. " ]]";
			end
		end

		-- Index part
		file:write( sprintf("\t%s = ", v.index))

		-- Value part
		if type(v.value) == "table" then -- if it's a table of bytes
			file:write( sprintf("{"))
			for i = 1, #v.value do
				if i ~= 1 then file:write( sprintf(", ")) end
				file:write( sprintf("0x%02X", v.value[i]))
			end
			file:write( sprintf("},"))
		else                             -- if it's an address or offset
			file:write( sprintf("0x%X,", v.value))
		end

		-- Comment part
		file:write( sprintf("%s\n", comment) );
	end

	file:write("}\n");

	file:close();

end
rewriteAddresses();
